﻿// video1.cpp : 定义应用程序的入口点。
//
#include "framework.h"
#include "video1.h"
#include<windows.h>
#include <windowsx.h>
#include<iostream>
#include<string>
#include <Commdlg.h>
#include "opencv2/opencv.hpp" 

using namespace std;

#define MAX_LOADSTRING 100

// 全局变量:
HINSTANCE hInst;                                // 当前实例
WCHAR szTitle[MAX_LOADSTRING];                  // 标题栏文本
WCHAR szWindowClass[MAX_LOADSTRING];            // 主窗口类名


//自定义全局变量
cv::Mat img1;                        //视频1帧
cv::Mat img2;                        //视频2帧
string path1;                        //视频1的文件路径
string path2;                        //视频2的文件路径
cv::VideoCapture capture1;           //视频1读取器
cv::VideoCapture capture2;           //视频2读取器

int frameNum1;                       //视频1帧数
int frameNum2;                       //视频2帧数
int currentFrame1 = 0;               //视频1当前帧
int currentFrame2 = 0;               //视频2当前帧


int beforex;                         //移动小窗
int beforey;
int afterx;
int aftery;
bool isMove = false;
bool shouldMove = false;

//画任意点
int paintx[1000];
int painty[1000];
int paintIndex = 0;
bool isPaint = false;

//画直线
int paintLineStartx[100];
int paintLineStarty[100];
int paintLineEndx[100];
int paintLineEndy[100];
int paintLineIndex = 0;
bool isPaintLine = false;

//画矩形
int paintRectStartx[100];
int paintRectStarty[100];
int paintRectEndx[100];
int paintRectEndy[100];
int paintRectIndex = 0;
bool isPaintRect = false;

//初始大小
int width2;
int height2;
int x2;
int y2;
int x22;
int y22;
HDC hdc22;

enum PlayState
{
    playing, paused, stopped
};
PlayState playState1 = PlayState::playing;       // 播放状态     
PlayState playState2 = PlayState::playing;       // 播放状态     

enum VideoEffect
{
    no, edge
};
VideoEffect vidEffect = VideoEffect::no;        // 视频画面效果

enum PiPState {
    nul, pip                                     // 画中画效果
};
PiPState pipState = PiPState::nul;

enum PaintState {
    noPaint, paint                                     // 绘图效果
};
PaintState paintState = PaintState::noPaint;

enum MoveState {
    noMove, move                                     // 拖动效果
};
MoveState moveState = MoveState::noMove;

// 此代码模块中包含的函数的前向声明:
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);
string get_path();
void playVideo(cv::Mat img, HDC hdc, HWND hwnd,int x, int y, int width, int height);






int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: 在此处放置代码。

    // 初始化全局字符串
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_VIDEO1, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // 执行应用程序初始化:
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }
   
    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_VIDEO1));

    MSG msg;

    // 主消息循环:
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int) msg.wParam;
}



//
//  函数: MyRegisterClass()
//
//  目标: 注册窗口类。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_VIDEO1));
    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_VIDEO1);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassExW(&wcex);
}

//
//   函数: InitInstance(HINSTANCE, int)
//
//   目标: 保存实例句柄并创建主窗口
//
//   注释:
//
//        在此函数中，我们在全局变量中保存实例句柄并
//        创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance; // 将实例句柄存储在全局变量中

   HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

   if (!hWnd)
   {
      return FALSE;
   }
   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);
   SetTimer(hWnd, 1, 40, NULL);
   return TRUE;
}

//
//  函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  目标: 处理主窗口的消息。
//
//  WM_COMMAND  - 处理应用程序菜单
//  WM_PAINT    - 绘制主窗口
//  WM_DESTROY  - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            // 分析菜单选择:
            switch (wmId)
            {
            case IDM_ABOUT:
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                break;
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
            case IDM_OPEN1:
                path1=get_path();
                if (path1!=""){
                    bool opened = capture1.open(path1);
                    frameNum1 = capture1.get(cv::CAP_PROP_FRAME_COUNT); //总帧数
                    if (opened){
                        capture1 >> img1; //获取第一帧图像并显示
                        InvalidateRect(hWnd, NULL, false); //激发WM_PAINT时间，让窗口重绘    
                    }
                    else{
                        MessageBox(
                            hWnd,
                            L"视频未能打开",
                            L"错误提示",
                            MB_OK
                        );
                    }
                }
                break;
            case IDM_OPEN2:
                path2 = get_path();
                if (path2!="") {
                    bool opened = capture2.open(path2);
                    frameNum2 = capture2.get(cv::CAP_PROP_FRAME_COUNT); //总帧数
                    if (opened) {
                        capture2 >> img2; //获取第一帧图像并显示
                        InvalidateRect(hWnd, NULL, false); //激发WM_PAINT时间，让窗口重绘
                    }
                    else {
                        MessageBox(
                            hWnd,
                            L"视频未能打开",
                            L"错误提示",
                            MB_OK
                        );
                    }
                }
                break;
            case IDM_PLAY1:
                playState1 = PlayState::playing;
                break;
            case IDM_PLAY2:
                playState2 = PlayState::playing;
                break;
            case IDM_PAUSE1:
                playState1 = PlayState::paused;
                break;
            case IDM_PAUSE2:
                playState2 = PlayState::paused;
                break;
            case IDM_STOP1:
                playState1 = PlayState::stopped;
                capture1.set(cv::VideoCaptureProperties::CAP_PROP_POS_FRAMES, 0);
                break;
            case IDM_STOP2:
                playState2 = PlayState::stopped;
                capture2.set(cv::VideoCaptureProperties::CAP_PROP_POS_FRAMES, 0);
                break;
            case IDM_PIPOPEN:
                pipState = PiPState::pip;
                break;
            case IDM_PIPCLOSE:
                pipState = PiPState::nul;
                break;
            case IDM_EDGEOPEN:
                vidEffect = VideoEffect::edge;
                break;
            case IDM_EDGECLOSE:
                vidEffect = VideoEffect::no;
                break;
            case IDM_MOVEOPEN:
                moveState = MoveState::move;
                break;
            case IDM_MOVECLOSE:
                moveState = MoveState::noMove;
                break;
            case IDM_PAINTOPEN:
                paintState = PaintState::paint;
                break;
            case IDM_PAINTCLOSE:
                paintState = PaintState::noPaint;
                break;
            case IDM_PAINTLINE:
                isPaintLine = true;
                isPaintRect = false;
                break;
            case IDM_PAINTPOINT:
                isPaintLine = false;
                isPaintRect = false;
                break;
            case IDM_PAINTRECT:
                isPaintLine = false;
                isPaintRect = true;
                break;
            case IDM_CLEARPAINT:
                paintIndex = 0;
                paintLineIndex = 0;
                paintRectIndex = 0;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;
    case WM_ERASEBKGND:return 1;
    case WM_LBUTTONDOWN:
        hdc22 = GetDC(hWnd);
        if (moveState == MoveState::move) {
            x22 = GET_X_LPARAM(lParam);
            y22 = GET_Y_LPARAM(lParam);
            if (x22 >= x2 && y22 >= y2 && x22 <= x2 + width2 && y22 <= y2 + height2) {
                isMove = true;
                beforex = x22;
                beforey = y22;
            }
        }
        if (paintState == PaintState::paint) {
            if (isPaintLine) {
                paintLineStartx[paintLineIndex] = GET_X_LPARAM(lParam);
                paintLineStarty[paintLineIndex] = GET_Y_LPARAM(lParam);
            }
            else if (isPaintRect) {
                paintRectStartx[paintRectIndex] = GET_X_LPARAM(lParam);
                paintRectStarty[paintRectIndex] = GET_Y_LPARAM(lParam);
            }
            else {
                isPaint = true;
                paintx[paintIndex] = GET_X_LPARAM(lParam);
                painty[paintIndex] = GET_Y_LPARAM(lParam);
            }
        }
        break;
    case WM_MOUSEMOVE:
        if (isPaint) {
            hdc22 = GetDC(hWnd);
            paintx[paintIndex] = GET_X_LPARAM(lParam);
            painty[paintIndex] = GET_Y_LPARAM(lParam);
            paintIndex++;
        }
        break;
    case WM_LBUTTONUP:
        hdc22 = GetDC(hWnd);
        //拖动小窗
        if (moveState == MoveState::move) {
            if (isMove) {
                afterx = GET_X_LPARAM(lParam);
                aftery = GET_Y_LPARAM(lParam);
                shouldMove = true;
                isMove = false;
            }
        }
        //画图
        if (paintState == PaintState::paint) {
            if (isPaintLine) {
                paintLineEndx[paintLineIndex] = GET_X_LPARAM(lParam);
                paintLineEndy[paintLineIndex] = GET_Y_LPARAM(lParam);
                paintLineIndex++;
            }
            else if (isPaintRect) {
                paintRectEndx[paintRectIndex] = GET_X_LPARAM(lParam);
                paintRectEndy[paintRectIndex] = GET_Y_LPARAM(lParam);
                paintRectIndex++;
            }
            else{
                isPaint = false;       
            }   
        }
        break;
    case WM_TIMER:
        if (capture1.isOpened() && playState1 == PlayState::playing){
            capture1 >> img1;
            if (++currentFrame1 >= frameNum1 - 1) {
                currentFrame1 = 0;
                capture1.set(cv::VideoCaptureProperties::CAP_PROP_POS_FRAMES, currentFrame1);
            }
            if (img1.empty() == false){
                InvalidateRect(hWnd, NULL, false);
            }
        }
        if (capture2.isOpened() && playState2 == PlayState::playing){
            capture2 >> img2;
            if (++currentFrame2 >= frameNum2 - 1) {
                currentFrame2 = 0;
                capture2.set(cv::VideoCaptureProperties::CAP_PROP_POS_FRAMES, currentFrame2);
            }
            if (img2.empty() == false){
                if (vidEffect == VideoEffect::edge){
                    cv::Mat edgeY, edgeX;
                    cv::Sobel(img2, edgeX, CV_8U, 0, 1);
                    img2 = edgeX;
                }
                InvalidateRect(hWnd, NULL, false);
            }
        }
        break;
    case WM_PAINT:{
            PAINTSTRUCT ps;
            HDC hdcOld = BeginPaint(hWnd, &ps);
            // TODO: 在此处添加使用 hdc 的任何绘图代码...
            RECT rect;
            GetClientRect(hWnd, &rect);
            HDC hdc = CreateCompatibleDC(hdcOld);
            //需要获取窗口的宽与高, 缓制尺寸
            RECT clientRect;
            GetClientRect(hWnd, &clientRect);
            //创建内存兼容位图hBmp
            HBITMAP hBmp = CreateCompatibleBitmap(hdcOld, clientRect.right, clientRect.bottom);
            //将内存位图选入缓冲内存DC中——以便可以绘制多个位图
            SelectObject(hdc, hBmp);//如果不执行这两步, 窗口显示出来会出现黑色背景 
            SelectObject(hdc, GetSysColorBrush(COLOR_3DFACE)); //设置刷子颜色 - Rectangle()的填充色
            Rectangle(hdc, -1, -1, clientRect.right + 2, clientRect.bottom + 2); //画窗体的整个背景
            if (pipState == PiPState::pip) {
                if (img1.cols !=0 &&img2.cols!=0) {
                    int wWidth = rect.right - rect.left;
                    int wHeight = rect.bottom - rect.top;
                    int width1 = img1.cols * wHeight / img1.rows;
                    int x1 = (wWidth - width1) / 2;
                    playVideo(img1, hdc, hWnd, x1, 0, width1, wHeight);
                    width2 = img2.cols * wHeight / img2.rows / 3;
                    if (moveState == MoveState::noMove) {
                        x2 = wWidth - x1 - width2;
                        y2 = 0;
                        height2 = wHeight / 3;
                    }
                    else {
                        if (shouldMove) {
                            x2 += afterx - beforex;
                            y2 += aftery - beforey;
                            shouldMove = false;
                        }
                    }
                    playVideo(img2, hdc, hWnd, x2, y2, width2, height2);
                }        
            }
            else {
                int wWidth = rect.right - rect.left;
                int wHeight = rect.bottom - rect.top;
                int width1 = wWidth / 2;
                int x1 = 0;
               
                if (img1.cols != 0) {
                    playVideo(img1, hdc, hWnd, x1, 0, width1, width1* img1.rows / img1.cols);
                }
                    
                if (img2.cols != 0) {
                    width2 = wWidth / 2;
                    x2 = width1;
                    height2 = width2 * img2.rows / img2.cols;
                    y2 = 0;
                    playVideo(img2, hdc, hWnd, x2, y2, width2, height2);
                }   
            }
            if (paintState == PaintState::paint) {
                for (int i = 0; i < paintIndex; i++) {
                   
                    SetPixel(hdc, paintx[i],painty[i], RGB(255, 0, 0));
                }
                for (int i = 0; i < paintLineIndex;i++) {
                    HPEN hpen1 = CreatePen(PS_SOLID, 1, RGB(255, 0, 0));//实线
                    HPEN oldhpen = (HPEN)SelectObject(hdc, hpen1);//选入画笔
                    MoveToEx(hdc, paintLineStartx[i], paintLineStarty[i], NULL);
                    LineTo(hdc, paintLineEndx[i], paintLineEndy[i] );
                    SelectObject(hdc, oldhpen);//恢复先前画笔
                    DeleteObject(hpen1);//记得自创画笔一定删除
                }
                for (int i = 0; i < paintRectIndex;i++) {
                    HBRUSH hbrush1 = CreateSolidBrush(RGB(255, 0, 0)); //绿色实心画刷
                    HPEN hpen = CreatePen(PS_NULL, 1, 0);  //创建空画笔
                    HPEN oldhpen = (HPEN)SelectObject(hdc, hpen);  //重点：填充图形的边框由当前画笔绘制
                    HBRUSH oldhbrush = (HBRUSH)SelectObject(hdc, hbrush1);//选入环境句柄
                    Rectangle(hdc, paintRectStartx[i], paintRectStarty[i], paintRectEndx[i], paintRectEndy[i]);
                    SelectObject(hdc, oldhpen);
                    SelectObject(hdc, oldhbrush);
                    DeleteObject(hbrush1);//删除画刷
                }
            }
            BitBlt(hdcOld, 0, 0, clientRect.right, clientRect.bottom, hdc, 0, 0, SRCCOPY);
            DeleteObject(hBmp);//回收内存资源
            DeleteDC(hdc);
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

// “关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}
string Lpcwstr2String(LPCWSTR lps) {
    int len = WideCharToMultiByte(CP_ACP, 0, lps, -1, NULL, 0, NULL, NULL);
    if (len <= 0) {
        return "";
    }
    else {
        char* dest = new char[len];
        WideCharToMultiByte(CP_ACP, 0, lps, -1, dest, len, NULL, NULL);
        dest[len - 1] = 0;
        string str(dest);
        delete[] dest;
        return str;
    }
}


//加载视频
void playVideo(cv::Mat img, HDC hdc,HWND hwnd,int x,int y,int width,int height) {
    //用于缓冲的内存DC
    if (img.rows > 0){
        switch (img.channels())
        {
        case 1:
            cv::cvtColor(img, img, cv::COLOR_GRAY2BGR); // GRAY单通道
            break;
        case 3:
            cv::cvtColor(img, img, cv::COLOR_BGR2BGRA);  // BGR三通道
            break;
        default:
            break;
        }

        int pixelBytes = img.channels() * (img.depth() + 1); // 计算一个像素多少个字节

                                                             // 制作bitmapinfo(数据头)
        BITMAPINFO bitInfo;
        bitInfo.bmiHeader.biBitCount = 8 * pixelBytes;
        bitInfo.bmiHeader.biWidth = img.cols;
        bitInfo.bmiHeader.biHeight = -img.rows;
        bitInfo.bmiHeader.biPlanes = 1;
        bitInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
        bitInfo.bmiHeader.biCompression = BI_RGB;
        bitInfo.bmiHeader.biClrImportant = 0;
        bitInfo.bmiHeader.biClrUsed = 0;
        bitInfo.bmiHeader.biSizeImage = 0;
        bitInfo.bmiHeader.biXPelsPerMeter = 0;
        bitInfo.bmiHeader.biYPelsPerMeter = 0;
        // Mat.data + bitmap数据头 -> MFC
        std::cout << width << "\n" << height;
        SetStretchBltMode(hdc, COLORONCOLOR);
        StretchDIBits(
            hdc,
            x,y,width,height,
            0, 0, img.cols, img.rows,
            img.data,
            &bitInfo,
            DIB_RGB_COLORS,
            SRCCOPY
        );
    }
}

//新建一个对话窗口，选择文件
string get_path() {
    OPENFILENAME ofn;
    char szFile[300];

    ZeroMemory(&ofn, sizeof(ofn));
    ofn.lStructSize = sizeof(ofn);
    ofn.hwndOwner = NULL;
    ofn.lpstrFile = (LPWSTR)szFile;
    ofn.lpstrFile[0] = '\0';
    LPTSTR        lpstrCustomFilter;
    DWORD         nMaxCustFilter;
    ofn.nFilterIndex = 1;
    LPTSTR        lpstrFile;
    ofn.nMaxFile = sizeof(szFile);

    ofn.lpstrFilter = L"AVI\0*.avi\0MP4\0*.mp4\0";
    ofn.lpstrFileTitle = NULL;
    ofn.nMaxFileTitle = 0;
    ofn.lpstrInitialDir = NULL;

    ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;

    string path = "";
    if (GetOpenFileName(&ofn)) {
        path = Lpcwstr2String(ofn.lpstrFile);
        return path;
    }
    else {
        return "";
    }
}
